#ifndef Stream_First
#define Stream_First
#ifdef __GNUG__
#pragma interface
#endif

#include "Types.h"

#ifndef BUFSIZE
#       define BUFSIZE 4096
#endif

#ifndef EOF
#       define EOF (-1)
#endif EOF

#define SP << ' '
#define NL << '\n'
#define TAB << '\t'

enum state_value {
	_good   = 0,
	_eof    = 1,
	_fail   = 2
};

class ObjArray;
class Object;
class OrdCollection;
class StreamBuf;
class IStream;

enum TypeTags {
	eTNone,
	eTChar,
	eTUChar,
	eTShort,
	eTUShort,
	eTInt,
	eTUInt,
	eTLong,
	eTULong,
	eTFloat,
	eTDouble,
	eTString,
	eTUString,
	eTStream,
	eTVoid
};

//---- Stream ------------------------------------------------------------------

class Stream {
protected:
	Stream(StreamBuf* s, bool dodelete= FALSE);
public:
	virtual ~Stream();

	void Push(StreamBuf *s);
	void Pop();

	inline operator void*();
	int operator!();
	int eof();
	int fail();
	int bad();
	int good();
	void setstate(state_value st);
	void clear();
	int rdstate();
	char* bufptr();
	StreamBuf* getsbuf();
	long tell();

	// object table management
	ObjArray *Table();
	int IndexOfPtr(Object *op);
	Object *At(int ix);
	void AtPut(int ix, Object *op);
	void PrepareForDeepClone();
	bool IsForDeepClone();
	int MakeIndex(Object *op);
	static void InvalidatePtr(Object*);

protected:
	ObjArray *it;
	bool deepclone;
	bool delete_StreamBuf;
	StreamBuf *bp;
	short state;
private:
	static OrdCollection *all;                              
};

//---- OStream -----------------------------------------------------------------

class OStream : public Stream {
public:
	OStream(StreamBuf* s, bool dodelete= FALSE);
	OStream(int fd);
	OStream(int fd, char *p, int size);
	OStream(int size, char *p);
	OStream(const char *path);
	~OStream();
		
	OStream &flush();
	OStream &form(char* va_alist, ...);
	OStream &put(char);
	OStream &put(u_char);
	OStream &seek(long pos, bool relative= FALSE);
	OStream &rewind();
	long tell();
	int write(const u_char *s, int n);
	int write(const char *s, int n);

	virtual OStream &Print(int code, void *vp);

	OStream &operator<< (char c);
	OStream &operator<< (u_char c);
	OStream &operator<< (const char *s);
	OStream &operator<< (const u_char *s);
	OStream &operator<< (long l);
	OStream &operator<< (u_long l);
	OStream &operator<< (short s);
	OStream &operator<< (u_short s);
	OStream &operator<< (int i);
	OStream &operator<< (u_int i);
	OStream &operator<< (double d);
	OStream &operator<< (float f);
	OStream &operator<< (IStream &s);
	//OStream &operator<< (void *v);

	void PutBigEndian(int bytes, long v);
	OStream &PrintString(const char *s, int l= -1);
	OStream &PrintString(const u_char *s, int l= -1);
	OStream &PrintHexString(const char *s, int l);
	OStream &PrintHexString(const u_char *s, int l);
};


//---- IStream -----------------------------------------------------------------

/*
The >> operator reads after skipping initial whitespace
get() reads but does not skip whitespace

if >> fails     (1) the state of the Stream turns non-zero
				(2) the value of the argument does not change
				(3) non-whitespace characters are put back onto the Stream

>> get() fails if the state of the Stream is non-zero
*/

class IStream : public Stream {
public:
	IStream(StreamBuf *s, OStream *t= 0, bool dodelete= FALSE);
					// bind to buffer
	IStream(const char *p, int l);  // bind to string
	IStream(int fd, OStream *t= 0); // bind to file
	IStream(const char *path);      // bind to file

	void flush();

	operator void*();
	void eatwhite();
	IStream &putback(char c);
	IStream &seek(long pos, bool relative= FALSE);
	IStream &rewind();
	long tell();
	OStream* tie(OStream* s);
	int read(u_char *s, int n);
	int read(char *s, int n);

	// raw input: get's do not skip whitespace
	IStream &get(char*, int, char= '\n');   // string
	IStream &get(char &c);  // single character
	IStream &get(u_char &b);        // single unsigned character

	IStream &getline(char *buf, int len, char term= '\n');
	IStream &getline(u_char *buf, int len, char term= '\n');

	IStream &peek(char &c);
	IStream &peek(u_char &b);

	// formatted input: >> skip whitespace
	virtual IStream &Scan(int code, void *vp);

	IStream &operator>> (char *v);
	IStream &operator>> (u_char *v);
	IStream &operator>> (char &v);
	IStream &operator>> (u_char &v);
	IStream &operator>> (short &v);
	IStream &operator>> (u_short &v);
	IStream &operator>> (int &v);
	IStream &operator>> (u_int &v);
	IStream &operator>> (long &v);
	IStream &operator>> (u_long &v);
	IStream &operator>> (float &v);
	IStream &operator>> (double &v);
	//IStream &operator>> (Stream &v);

	long GetBigEndian(int bytes);
	char GetChar();
	u_char GetByte();

	u_char GetHex();
	IStream &ReadString(char **s, int *lp= 0);
	IStream &ReadString(u_char **s, int *lp= 0);

protected:
	OStream *tied_to;
};

extern IStream gCin;     // standard input predefined
extern OStream gCout;    // standard output
extern OStream gCerr;    // error output

//---- inlines -----------------------------------------------------------------

inline Stream::operator void*()
	{ return _eof < state ? 0 : this; }
inline int Stream::operator!()
	{ return _eof < state; }
inline int Stream::eof()
	{ return state  &_eof; }
inline int Stream::fail()
	{ return _eof < state; }
inline int Stream::bad()
	{ return _fail < state; }
inline int Stream::good()
	{ return state == _good; }
inline void Stream::setstate(state_value st)
	{ state= st; }
inline void Stream::clear()
	{ state= _good; }
inline int Stream::rdstate()
	{ return state; }
inline StreamBuf *Stream::getsbuf()
	{ return bp; }
inline void Stream::PrepareForDeepClone()
	{ deepclone= TRUE; }
inline bool Stream::IsForDeepClone()
	{ return deepclone; }


inline OStream &OStream::rewind()
	{ return seek(0); }
inline int OStream::write(const char *s, int n)
	{ return write((const u_char*)s, n); }
inline OStream &OStream::operator<< (char c)
	{ return Print(eTChar, (void*) c); }
inline OStream &OStream::operator<< (u_char c)
	{ return Print(eTUChar, (void*) c); }
inline OStream &OStream::operator<< (const char *s)
	{ return Print(eTString, (void*) s); }
inline OStream &OStream::operator<< (const u_char *s)
	{ return Print(eTUString, (void*) s); }
inline OStream &OStream::operator<< (long l)
	{ return Print(eTLong, (void*) l); }
inline OStream &OStream::operator<< (u_long l)
	{ return Print(eTULong, (void*) l); }
inline OStream &OStream::operator<< (short s)
	{ return Print(eTShort, (void*) s); }
inline OStream &OStream::operator<< (u_short s)
	{ return Print(eTUShort, (void*) s); }
inline OStream &OStream::operator<< (int i)
	{ return Print(eTInt, (void*) i); }
inline OStream &OStream::operator<< (u_int i)
	{ return Print(eTUInt, (void*) i); }
inline OStream &OStream::operator<< (double d)
	{ return Print(eTDouble, (void*) &d); }
inline OStream &OStream::operator<< (float f)
	{ return Print(eTFloat, (void*) &f); }
inline OStream &OStream::operator<< (IStream &s)
	{ return Print(eTStream, (void*) &s); }
//inline OStream &OStream::operator<< (void *v)
//    { return Print(eTVoid, v); }
inline OStream &OStream::PrintString(const u_char *s, int l)
	{ return PrintString((const char*)s, l); }
inline OStream &OStream::PrintHexString(const u_char *s, int l)
	{ return PrintHexString((const char*) s, l); }

inline void IStream::flush()
	{ if (tied_to) tied_to->flush(); }
inline IStream::operator void*()
	{ return Stream::operator void*(); }
inline IStream &IStream::rewind()
	{ return seek(0); }
inline int IStream::read(char *s, int n)
	{ return read((u_char*)s, n); }
inline IStream &IStream::operator>> (char *v)
	{ return Scan(eTString, v); }
inline IStream &IStream::operator>> (u_char *v)
	{ return Scan(eTUString, v); }
inline IStream &IStream::operator>> (char &v)
	{ return Scan(eTChar, &v); }
inline IStream &IStream::operator>> (u_char &v)
	{ return Scan(eTUChar, &v); }
inline IStream &IStream::operator>> (short &v)
	{ return Scan(eTShort, &v); }
inline IStream &IStream::operator>> (u_short &v)
	{ return Scan(eTUShort, &v); }
inline IStream &IStream::operator>> (int &v)
	{ return Scan(eTInt, &v); }
inline IStream &IStream::operator>> (u_int &v)
	{ return Scan(eTUInt, &v); }
inline IStream &IStream::operator>> (long &v)
	{ return Scan(eTLong, &v); }
inline IStream &IStream::operator>> (u_long &v)
	{ return Scan(eTULong, &v); }
inline IStream &IStream::operator>> (float &v)
	{ return Scan(eTFloat, &v); }
inline IStream &IStream::operator>> (double &v)
	{ return Scan(eTDouble, &v); }
//inline IStream &IStream::operator>> (Stream &v)
//    { return Scan(eTStream, &v); }
inline IStream &IStream::ReadString(u_char **s, int *lp)
	{ return ReadString((char**)s, lp); }
inline IStream &IStream::getline(u_char *buf, int len, char term)
	{ return getline((char*)buf, len, term); }

#endif
